#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# test put/get/del()
#

import sys,signal,traceback
import os, time

from openchord import ID
from openchord import URL, Chord, Node
from openchord.help import logging

configure = ''' 
logging.rootLogger = DEBUG, A1, A2
logging.appender.A1 = ConsoleAppender
logging.appender.A1.layout = NotImplemented
logging.appender.A2 = FileAppender
logging.appender.A2.file = /tmp/test-chord.log
logging.appender.A2.layout = NotImplemented
'''
logging.configure( configure )

logger = logging.getLogger('OpenChord')
#logger.setLevel(logging.LEVEL_DEBUG)

chord = None
def signalHandler(signum, frame) :
    logger.debug('catch Signal #%d ...', signum)
    if chord :
        logger.debug('chord will stop ...')
        chord.stop()
    signal.signal(signum, signalHandler)
    ## signalHandler()
signal.signal(signal.SIGTERM, signalHandler)

def signalChildReaper(signum, frame) :
    logger.debug('catch Signal #%d, reaping child ...', signum)
    #logger.debug(str(frame))
    ## signalChildReaper()
signal.signal(signal.SIGCHLD, signalChildReaper)


ports = ((43000,0), (43030, 3), (43050, 5),)
chord_root_port = ports[0][0]
chord_root_url = 'octcp://127.0.0.1:43000/test'


class   ChordNode(object) :
    def __init__(self, port, id) :
        self.chord_port = port
        self.chord_id = id
        if chord_root_port == port :
            self.chord_root_url = None
        else :
            self.chord_root_url = chord_root_url
        self.chord_url = 'octcp://127.0.0.1:%d/test' % port

        self.__will_stop = False
        ## __init__()

    def start(self) :
        self.run()
        ## start()

    def run(self) :
        chord = Chord()
        chord.join(self.chord_root_url, self.chord_url, self.chord_id)
        while not self.__will_stop :
            try :
                time.sleep(5)
                while chord.node.when_finger_last_check < chord.node.when_finger_last_update :
                    logger.debug('UPADTE TIME: %s', time.ctime(chord.node.when_finger_last_update))
                    logger.debug('CHECK  TIME: %s', time.ctime(chord.node.when_finger_last_check))
                    time.sleep(1)
                self.testPUT(chord.node)
            except :
                exstr = traceback.format_exc()
                logger.error('%s', str(exstr))

        chord.leave()
        chord = None
        ## run()

    def stop(self) :
        self.__will_stop = True
        ## stop()

    def testPUT(self, node) :
        count_error = 0
        for i in range(1, 5, 1) :
            key = 'HELLO-%d-%d' % (node.id_n, i)
            data = 'WORLD-%d-%d' % (node.id_n, i)
            op_status,op_key = node.put(key, data)
            if 200 != op_status :
                logger.error('on Node#%d PUT(%s, %s) status:%d', node.id_n, key, data, op_status)
                count_error += 1

        if count_error > 0 :
            logger.info('on Node#%d TEST-Lookup failed (%d errors).', node.id_n, count_error)
        else :
            logger.info('on Node#%d TEST-Lookup succeed.', node.id_n)
        ## testPUT()


    ## class ChordNode


children = []

for port,id in ports :
    pid = os.fork()
    if pid == 0 :
        # child
        signal.signal(signal.SIGTERM, signalHandler)
        chord = ChordNode(port, id)
        chord.start()

        sys.exit(0)
    else :
        children.append( pid )
        try :
            time.sleep(5)
        except :
            pass

try :
    os.wait()
except KeyboardInterrupt :
    print 'Keyboard Interrupt. ending ...'
    for pid in children :
        try :
            os.kill(pid, signal.SIGTERM)
        except :
            pass
except :
    for pid in children :
        try :
            os.kill(pid, signal.SIGTERM)
        except :
            pass

kill_count = 0
while kill_count < 5 :
    children_alive = []
    for pid in children :
        try :
            #logger.debug('killing #%d pid ...', pid)
            ret = os.kill(pid, 0)
            #logger.debug('killing #%d pid return %s', pid, str(ret))
            children_alive.append(pid)
            for signum in (signal.SIGTERM, signal.SIGQUIT) :
                #logger.debug('killing #%d pid with SIGNAL #%d ...', pid, signum)
                os.kill(pid, signum)
        except :
            exstr = traceback.format_exc()
            #logger.error(exstr)

    children = children_alive
    time.sleep(1)
    kill_count += 1

#for pid in children :
#    try :
#        #logger.debug('killing #%d pid with SIGNAL #%d ...', pid, signal.SIGKILL)
#        os.kill(pid, signal.SIGKILL)
#    except :
#        exstr = traceback.format_exc()
#        logger.error(exstr)

logger.info('done.')
sys.exit(0)
    

